//
//  DGScrollViewWrapper.m
//  DGUIKit
//
//  Created by Jinxiao on 4/11/13.
//  Copyright (c) 2013 debugeek. All rights reserved.
//

#import "DGScrollViewWrapper.h"

@interface DGScrollViewWrapper ()

@property (readwrite, nonatomic, weak) UIScrollView *scrollView;
@property (readwrite, nonatomic, assign) DGScrollViewStatus status;
@property (readwrite, nonatomic, assign) BOOL animationLock;
@property (readwrite, nonatomic, assign) BOOL initialized;
@property (readwrite, nonatomic, strong) UIView *headerView;
@property (readwrite, nonatomic, strong) UIView *footerView;

@end

@implementation DGScrollViewWrapper

- (id)initWithScrollView:(UIScrollView *)scrollView {
    self = [super init];
    {
        self.scrollView = scrollView;
        self.originalInset = scrollView.contentInset;
        self.supportLoadNew = YES;
        self.supportLoadMore = YES;

        [scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    }

    return self;
}

- (void)dealloc {
    [self.scrollView removeObserver:self forKeyPath:@"contentSize"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if([keyPath isEqualToString:@"contentSize"]) {
        CGSize newSize = [[change objectForKey:@"new"] CGSizeValue];
        CGSize oldSize = [[change objectForKey:@"old"] CGSizeValue];

        if(!CGSizeEqualToSize(newSize, oldSize)) {
            [self layoutHeaderView];
            [self layourFooterView];
        }
    }
}

- (void)setEnableLoadNew:(BOOL)enableLoadNew {
    if(!self.supportLoadNew || self.enableLoadNew == enableLoadNew) {
        return;
    }

    if(self.initialized) {
        [self configureEnableLoadNew:enableLoadNew];
    } else {
        _enableLoadNew = enableLoadNew;
    }
}

- (void)setEnableLoadMore:(BOOL)enableLoadMore {
    if(!self.supportLoadMore || self.enableLoadMore == enableLoadMore) {
        return;
    }

    if(self.initialized) {
        [self configureEnableLoadMore:enableLoadMore];
    } else {
        _enableLoadMore = enableLoadMore;
    }
}

- (void)configureEnableLoadNew:(BOOL)enableLoadNew {
    _enableLoadNew = enableLoadNew;

    if(![self isLoadingNew]) {
        self.headerView.hidden = !self.enableLoadNew;
    }
}

- (void)configureEnableLoadMore:(BOOL)enableLoadMore {
    _enableLoadMore = enableLoadMore;

    if(![self isLoadingMore]) {
        self.footerView.hidden = !self.enableLoadMore;
    }
}

- (BOOL)isLoadingNew {
    return (self.supportLoadNew && self.enableLoadNew && self.status == DGScrollViewStatusLoadingNew);
}

- (BOOL)isLoadingMore {
    return (self.supportLoadMore && self.enableLoadMore && self.status == DGScrollViewStatusLoadingMore);
}

- (void)initialize {
    if(self.supportLoadNew) {
        self.headerView = [[UIView alloc] init];
        [self.scrollView addSubview:self.headerView];

        [self configureEnableLoadNew:self.enableLoadNew];
        [self layoutHeaderView];
    }

    if(self.supportLoadMore) {
        self.footerView = [[UIView alloc] init];
        [self.scrollView addSubview:self.footerView];

        [self configureEnableLoadMore:self.enableLoadMore];
        [self layourFooterView];
    }

    UIEdgeInsets contentInset = self.originalInset;
    CGPoint contentOffset = self.scrollView.contentOffset;
    contentOffset.y -= contentInset.top;
//    [self setScrollViewContentInset:contentInset contentOffset:contentOffset animated:NO completion:nil];

    self.scrollView.scrollIndicatorInsets = self.originalInset;

    self.initialized = YES;
}

- (BOOL)shouldTriggerLoadNew {
    return ([self topOffset] <= -kLoadNewThreshold);
}

- (BOOL)shouldTriggerLoadMore {
    return ([self bottomOffset] >= kLoadMoreThreshold);
}

- (void)layoutHeaderView {
    self.headerView.frame = CGRectMake(0.f, -kLoadNewHeaderHeight, self.scrollView.frame.size.width, kLoadNewHeaderHeight);
}

- (void)layourFooterView {
    self.footerView.frame = CGRectMake(0.f, MAX(self.scrollView.contentSize.height, self.scrollView.frame.size.height - self.originalInset.top - self.originalInset.bottom), self.scrollView.frame.size.width, kLoadMoreFooterHeight);
}

- (CGFloat)topOffset {
    return self.scrollView.contentOffset.y + self.originalInset.top;
}

- (CGFloat)bottomOffset {
    CGFloat bottomOffset = 0.f;

    if(self.scrollView.contentSize.height + self.originalInset.top + self.originalInset.bottom > self.scrollView.frame.size.height) {
        bottomOffset = self.scrollView.contentOffset.y + self.scrollView.frame.size.height - self.scrollView.contentSize.height - self.originalInset.bottom;
    } else {
        bottomOffset = self.scrollView.contentOffset.y + self.originalInset.top;
    }

    return bottomOffset;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat topOffset = [self topOffset];
    CGFloat bottomOffset = [self bottomOffset];

    if(!self.animationLock) {
        if(topOffset < 0.f) {
            [self performAnimationWithStatus:self.status action:DGScrollViewActionLoadNew progress:-topOffset/kLoadNewThreshold];
        } else if(bottomOffset > 0.f) {
            [self performAnimationWithStatus:self.status action:DGScrollViewActionLoadMore progress:bottomOffset/kLoadMoreThreshold];
        }
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView {
    if([self shouldTriggerLoadNew]) {
        [self triggerLoadNew];
    } else if([self shouldTriggerLoadMore]) {
        [self triggerLoadMore];
    }
}

- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset contentOffset:(CGPoint)contentOffset completion:(void (^)(void))completion {
    [self setScrollViewContentInset:contentInset contentOffset:contentOffset animated:YES completion:completion];
}

- (void)setScrollViewContentInset:(UIEdgeInsets)contentInset contentOffset:(CGPoint)contentOffset animated:(BOOL)animated completion:(void (^)(void))completion {
    if(animated) {
        [UIView animateWithDuration:.35f delay:0.f options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
            self.scrollView.contentInset = contentInset;
            self.scrollView.contentOffset = contentOffset;
            self.scrollView.scrollEnabled = NO;
            self.scrollView.scrollEnabled = YES;
        } completion:^(BOOL finished) {
            if(completion) {
                completion();
            }
        }];
    } else {
        self.scrollView.contentInset = contentInset;
        self.scrollView.contentOffset = contentOffset;

        if(completion) {
            completion();
        }
    }
}

- (void)setStatus:(DGScrollViewStatus)status {
    if(self.status != status && !self.animationLock) {
        self.animationLock = YES;

        if(status == DGScrollViewStatusNormal) {
            if(self.status == DGScrollViewStatusLoadingNew) {
                self->_status = status;

                [self performAnimationWithStatus:status action:DGScrollViewActionLoadNew progress:0.f];

                UIEdgeInsets contentInset = self.originalInset;
                CGPoint contentOffset = self.scrollView.contentOffset;

                if(contentOffset.y < 0.f) {
                    contentOffset.y = -self.originalInset.top;
                }

                __weak typeof( & *self) weakSelf = self;
                [self setScrollViewContentInset:contentInset contentOffset:contentOffset completion:^{
                    __strong typeof(&*weakSelf) strongSelf = weakSelf;

                    if(strongSelf) {
                        strongSelf.animationLock = NO;
                    }
                }];
            } else if(self.status == DGScrollViewStatusLoadingMore) {
                self->_status = status;

                [self performAnimationWithStatus:status action:DGScrollViewActionLoadMore progress:0.f];

                UIEdgeInsets contentInset = self.originalInset;
                CGPoint contentOffset = self.scrollView.contentOffset;

                if(self.scrollView.contentSize.height <= self.scrollView.frame.size.height) {
                    contentOffset.y = -contentInset.top;
                } else if(contentOffset.y + self.scrollView.frame.size.height > self.scrollView.contentSize.height) {
                    contentOffset.y = self.scrollView.contentSize.height - self.scrollView.frame.size.height + contentInset.bottom;
                }

                __weak typeof( & *self) weakSelf = self;
                [self setScrollViewContentInset:contentInset contentOffset:contentOffset completion:^{
                    __strong typeof(&*weakSelf) strongSelf = weakSelf;

                    if(strongSelf) {
                        strongSelf.animationLock = NO;
                    }
                }];
            }
        } else if(status == DGScrollViewStatusLoadingNew) {
            [self performAnimationWithStatus:status action:DGScrollViewActionLoadNew progress:1.f];

            UIEdgeInsets contentInset = self.originalInset;
            contentInset.top += kLoadNewHeaderHeight;
            CGPoint contentOffset = self.scrollView.contentOffset;
            contentOffset.y = -kLoadNewHeaderHeight - self.originalInset.top;

            __weak typeof( & *self) weakSelf = self;
            [self setScrollViewContentInset:contentInset contentOffset:contentOffset completion:^{
                __strong typeof(&*weakSelf) strongSelf = weakSelf;

                if(strongSelf) {
                    strongSelf->_status = status;
                    strongSelf.animationLock = NO;
                    [strongSelf performActionWithScrollViewStatus:status];
                }
            }];
        } else if(status == DGScrollViewStatusLoadingMore) {
            [self performAnimationWithStatus:status action:DGScrollViewActionLoadMore progress:1.f];

            UIEdgeInsets contentInset = self.originalInset;

            if(self.scrollView.contentSize.height + self.originalInset.top + self.originalInset.bottom > self.scrollView.frame.size.height) {
                contentInset.bottom = kLoadMoreFooterHeight + self.originalInset.bottom;
            } else {
                contentInset.bottom = kLoadMoreFooterHeight + self.scrollView.frame.size.height - self.scrollView.contentSize.height - contentInset.top;
            }

            CGPoint contentOffset = self.scrollView.contentOffset;

            __weak typeof( & *self) weakSelf = self;
            [self setScrollViewContentInset:contentInset contentOffset:contentOffset completion:^{
                __strong typeof(&*weakSelf) strongSelf = weakSelf;

                if(strongSelf) {
                    strongSelf->_status = status;
                    strongSelf.animationLock = NO;
                    [strongSelf performActionWithScrollViewStatus:status];
                }
            }];
        }
    }
}

- (void)performActionWithScrollViewStatus:(DGScrollViewStatus)scrollViewStatus {
    if(scrollViewStatus == DGScrollViewStatusLoadingNew && [self.delegate respondsToSelector:@selector(scrollViewWrapperDidTriggerLoadNew:)]) {
        [self.delegate scrollViewWrapperDidTriggerLoadNew:self];
    }

    if(scrollViewStatus == DGScrollViewStatusLoadingMore && [self.delegate respondsToSelector:@selector(scrollViewWrapperDidTriggerLoadMore:)]) {
        [self.delegate scrollViewWrapperDidTriggerLoadMore:self];
    }
}

- (void)performAnimationWithStatus:(DGScrollViewStatus)status action:(DGScrollViewAction)action progress:(float)progress {
    if([self.delegate respondsToSelector:@selector(scrollViewWrapper:animatedWithStatus:action:actionView:progress:)]) {
        UIView *actionView = nil;

        if(action == DGScrollViewActionLoadNew) {
            actionView = self.headerView;
        } else {
            actionView = self.footerView;
        }

        [self.delegate scrollViewWrapper:self animatedWithStatus:status action:action actionView:actionView progress:progress];
    }
}

- (void)triggerLoadNew {
    BOOL shouldTrigger = YES;

    if([_delegate respondsToSelector:@selector(scrollViewWrapperShouldTriggerLoadNew:)]) {
        shouldTrigger = [_delegate scrollViewWrapperShouldTriggerLoadNew:self];
    }

    if(shouldTrigger && self.enableLoadNew && ![self isLoadingNew] && ![self isLoadingMore]) {
        self.status = DGScrollViewStatusLoadingNew;
    }
}

- (void)triggerLoadMore {
    BOOL shouldTrigger = YES;

    if([_delegate respondsToSelector:@selector(scrollViewWrapperShouldTriggerLoadMore:)]) {
        shouldTrigger = [_delegate scrollViewWrapperShouldTriggerLoadMore:self];
    }

    if(shouldTrigger && self.enableLoadMore && ![self isLoadingNew] && ![self isLoadingMore]) {
        self.status = DGScrollViewStatusLoadingMore;
    }
}

- (void)finishLoading {
    self.status = DGScrollViewStatusNormal;
}

@end
